package com.zsw.proxy;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Client {
	
	static final Logger LOG = LoggerFactory.getLogger(Client.class);
	static final boolean SSL = System.getProperty("ssl") != null;
    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8899"));
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
    
    private Channel ch;
    
    private ClientHandler handler ;
    
    private CountDownLatch countDown = new CountDownLatch(1);
    
	public String nettyClient(EventLoopGroup group) throws InterruptedException {
        Bootstrap b = new Bootstrap();
        handler = new ClientHandler(countDown);
        b.group(group)
         .channel(NioSocketChannel.class)
         .option(ChannelOption.TCP_NODELAY, true)
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ch.pipeline().addLast(
                		 new StringEncoder(CharsetUtil.UTF_8),
                         new StringDecoder(CharsetUtil.UTF_8),
//                         new LineBasedFrameDecoder(8192),
//                         new ChunkedWriteHandler(),
//                		 new LoggingHandler(LogLevel.WARN),
                		 handler);
             }
         });
        ch = b.connect(HOST, PORT).sync().channel();
        return new InvocationHandler_().bind(Test.class,countDown).test("zsw");
	}
    class InvocationHandler_ implements InvocationHandler{
    	private Class<?> clazz ;
    	private CountDownLatch countDown;
		@SuppressWarnings("unchecked")
		public <T> T bind(Class<T> clazz,CountDownLatch c){
			this.clazz = clazz;
			this.countDown = c;
    		return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, this);
    	}
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			String ss = clazz.getName()+";"+method.getName();
			for(Class<?> c : method.getParameterTypes()){
				ss += ";"+c.getName();
			}
			if(args.length > 0){
				ss += "#";
				for(Object o :args){
					ss += o.toString() + ";";
				}
				ss = ss.substring(0,ss.length() - 1);
			}
			ch.writeAndFlush(ss).addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					LOG.info("内容发送成功");
				}
			});
			countDown.await();
			return handler.qu.take();
		}
    }
    
    public String excute() {
    	EventLoopGroup group = new NioEventLoopGroup();
    	String ss = null;
		try {
			ss = group.submit(new Callable<String>() {
				@Override
				public String call() throws Exception {
					return nettyClient(group);
				}
			}).get(1, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		} finally {
			while(countDown.getCount() != 0){
				countDown.countDown();
			}
			group.shutdownGracefully();
		}
		return ss;
	}
    
    public static void main(String[] args) {
    	Client client = new Client();
    	System.out.println(client.excute());
    }
}
